package com.powerbank.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.powerbank.common.entity.User;
import com.powerbank.common.entity.UserIdentity;
import com.powerbank.common.exception.BusinessException;
import com.powerbank.common.result.ResultCode;
import com.powerbank.common.utils.JwtUtils;
import com.powerbank.common.utils.RedisUtils;
import com.powerbank.user.dto.UserLoginDTO;
import com.powerbank.user.dto.UserRegisterDTO;
import com.powerbank.user.mapper.UserIdentityMapper;
import com.powerbank.user.mapper.UserMapper;
import com.powerbank.user.service.AuthService;
import com.powerbank.user.vo.UserInfoVO;
import com.powerbank.user.vo.UserLoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * 用户认证服务实现类
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserIdentityMapper userIdentityMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisUtils redisUtils;

    @Value("${jwt.expiration}")
    private Long jwtExpiration;

    private static final String VERIFY_CODE_PREFIX = "verify_code:";
    private static final int VERIFY_CODE_EXPIRE = 300; // 5分钟

    @Override
    public boolean sendVerifyCode(String phone) {
        // 检查发送频率限制
        String cacheKey = VERIFY_CODE_PREFIX + phone;
        if (redisUtils.hasKey(cacheKey)) {
            throw new BusinessException(ResultCode.OPERATION_TOO_FREQUENT);
        }

        // 生成6位验证码
        String verifyCode = RandomUtil.randomNumbers(6);
        
        // 缓存验证码
        redisUtils.set(cacheKey, verifyCode, VERIFY_CODE_EXPIRE);
        
        // TODO: 实际发送短信验证码
        log.info("发送验证码到手机号: {}, 验证码: {}", phone, verifyCode);
        
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserLoginVO register(UserRegisterDTO registerDTO) {
        // 验证两次密码是否一致
        if (!registerDTO.getPassword().equals(registerDTO.getConfirmPassword())) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "两次密码不一致");
        }

        // 验证验证码
        String cacheKey = VERIFY_CODE_PREFIX + registerDTO.getPhone();
        String cachedCode = (String) redisUtils.get(cacheKey);
        if (StrUtil.isBlank(cachedCode) || !cachedCode.equals(registerDTO.getVerifyCode())) {
            throw new BusinessException(ResultCode.VERIFICATION_CODE_ERROR);
        }

        // 检查手机号是否已存在
        LambdaQueryWrapper<User> phoneWrapper = new LambdaQueryWrapper<>();
        phoneWrapper.eq(User::getPhone, registerDTO.getPhone());
        if (userMapper.selectCount(phoneWrapper) > 0) {
            throw new BusinessException(ResultCode.PHONE_NUMBER_EXIST);
        }

        // 检查用户名是否已存在
        LambdaQueryWrapper<User> usernameWrapper = new LambdaQueryWrapper<>();
        usernameWrapper.eq(User::getUsername, registerDTO.getUsername());
        if (userMapper.selectCount(usernameWrapper) > 0) {
            throw new BusinessException(ResultCode.USER_ALREADY_EXIST);
        }

        // 创建用户
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setPhone(registerDTO.getPhone());
        user.setPassword(passwordEncoder.encode(registerDTO.getPassword()));
        user.setNickname(registerDTO.getUsername());
        user.setStatus(1); // 正常状态
        user.setRegisterSource(registerDTO.getRegisterSource());
        user.setCreatedTime(LocalDateTime.now());
        user.setUpdatedTime(LocalDateTime.now());

        userMapper.insert(user);

        // 删除验证码缓存
        redisUtils.del(cacheKey);

        // 自动登录
        return generateLoginResult(user);
    }

    @Override
    public UserLoginVO login(UserLoginDTO loginDTO) {
        User user = null;

        if (loginDTO.getLoginType() == 1) {
            // 密码登录
            if (StrUtil.isBlank(loginDTO.getPassword())) {
                throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "密码不能为空");
            }
            user = loginByPassword(loginDTO.getAccount(), loginDTO.getPassword());
        } else if (loginDTO.getLoginType() == 2) {
            // 验证码登录
            if (StrUtil.isBlank(loginDTO.getVerifyCode())) {
                throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "验证码不能为空");
            }
            user = loginByVerifyCode(loginDTO.getAccount(), loginDTO.getVerifyCode());
        } else {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "不支持的登录类型");
        }

        // 更新登录信息
        user.setLastLoginTime(LocalDateTime.now());
        userMapper.updateById(user);

        return generateLoginResult(user);
    }

    @Override
    public UserLoginVO refreshToken(String refreshToken) {
        if (!jwtUtils.validateToken(refreshToken)) {
            throw new BusinessException(ResultCode.TOKEN_INVALID);
        }

        Long userId = jwtUtils.getUserIdFromToken(refreshToken);
        String username = jwtUtils.getUsernameFromToken(refreshToken);
        String userType = jwtUtils.getUserTypeFromToken(refreshToken);

        // 生成新的访问令牌
        String newAccessToken = jwtUtils.generateToken(userId, username, userType);
        String newRefreshToken = jwtUtils.generateRefreshToken(userId, username, userType);

        UserLoginVO loginVO = new UserLoginVO();
        loginVO.setAccessToken(newAccessToken);
        loginVO.setRefreshToken(newRefreshToken);
        loginVO.setExpiresIn(jwtExpiration);

        return loginVO;
    }

    @Override
    public void logout(Long userId) {
        // 删除Redis中的用户token缓存
        String tokenKey = RedisUtils.getUserTokenKey(userId);
        redisUtils.del(tokenKey);
    }

    @Override
    public UserInfoVO getCurrentUserInfo(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);

        // 查询实名认证信息
        LambdaQueryWrapper<UserIdentity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserIdentity::getUserId, userId);
        UserIdentity identity = userIdentityMapper.selectOne(wrapper);
        if (identity != null) {
            userInfoVO.setVerifyStatus(identity.getVerifyStatus());
            userInfoVO.setVerifyLevel(identity.getVerifyLevel());
        } else {
            userInfoVO.setVerifyStatus(0); // 未认证
            userInfoVO.setVerifyLevel(1); // 初级
        }

        return userInfoVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changePassword(Long userId, String oldPassword, String newPassword) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        // 验证旧密码
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            throw new BusinessException(ResultCode.PASSWORD_ERROR);
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        user.setUpdatedTime(LocalDateTime.now());
        
        return userMapper.updateById(user) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resetPassword(String phone, String verifyCode, String newPassword) {
        // 验证验证码
        String cacheKey = VERIFY_CODE_PREFIX + phone;
        String cachedCode = (String) redisUtils.get(cacheKey);
        if (StrUtil.isBlank(cachedCode) || !cachedCode.equals(verifyCode)) {
            throw new BusinessException(ResultCode.VERIFICATION_CODE_ERROR);
        }

        // 查找用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, phone);
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            throw new BusinessException(ResultCode.PHONE_NUMBER_NOT_EXIST);
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        user.setUpdatedTime(LocalDateTime.now());

        // 删除验证码缓存
        redisUtils.del(cacheKey);

        return userMapper.updateById(user) > 0;
    }

    /**
     * 密码登录
     */
    private User loginByPassword(String account, String password) {
        // 查找用户（支持用户名和手机号登录）
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.and(w -> w.eq(User::getUsername, account).or().eq(User::getPhone, account));
        User user = userMapper.selectOne(wrapper);

        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        // 验证密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BusinessException(ResultCode.USERNAME_OR_PASSWORD_ERROR);
        }

        // 检查用户状态
        checkUserStatus(user);

        return user;
    }

    /**
     * 验证码登录
     */
    private User loginByVerifyCode(String phone, String verifyCode) {
        // 验证验证码
        String cacheKey = VERIFY_CODE_PREFIX + phone;
        String cachedCode = (String) redisUtils.get(cacheKey);
        if (StrUtil.isBlank(cachedCode) || !cachedCode.equals(verifyCode)) {
            throw new BusinessException(ResultCode.VERIFICATION_CODE_ERROR);
        }

        // 查找用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone, phone);
        User user = userMapper.selectOne(wrapper);

        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_EXIST);
        }

        // 检查用户状态
        checkUserStatus(user);

        // 删除验证码缓存
        redisUtils.del(cacheKey);

        return user;
    }

    /**
     * 检查用户状态
     */
    private void checkUserStatus(User user) {
        if (user.getStatus() == 0) {
            throw new BusinessException(ResultCode.USER_ACCOUNT_DISABLED);
        } else if (user.getStatus() == 2) {
            throw new BusinessException(ResultCode.USER_ACCOUNT_LOCKED);
        }
    }

    /**
     * 生成登录结果
     */
    private UserLoginVO generateLoginResult(User user) {
        // 生成JWT令牌
        String accessToken = jwtUtils.generateToken(user.getId(), user.getUsername(), "USER");
        String refreshToken = jwtUtils.generateRefreshToken(user.getId(), user.getUsername(), "USER");

        // 缓存用户token
        String tokenKey = RedisUtils.getUserTokenKey(user.getId());
        redisUtils.set(tokenKey, accessToken, jwtExpiration);

        // 构建返回结果
        UserLoginVO loginVO = new UserLoginVO();
        loginVO.setAccessToken(accessToken);
        loginVO.setRefreshToken(refreshToken);
        loginVO.setExpiresIn(jwtExpiration);

        // 设置用户信息
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(user, userInfoVO);
        userInfoVO.setVerifyStatus(0); // 默认未认证
        userInfoVO.setVerifyLevel(1); // 默认初级
        loginVO.setUserInfo(userInfoVO);

        return loginVO;
    }
}